package config

import (
	"bytes"
	"encoding/base64"
	"fmt"
	"gopkg.in/yaml.v3"
	"os"
	"path"
	"path/filepath"
	"runtime"
	"strings"
)

// Config 总配置项
type Config struct {
	Mode       string `yaml:"mode"`
	ListenPort int    `yaml:"listen_port"`
	LogLevel   string `yaml:"log_level"`
	Pg         PG     `yaml:"pg"`
	Redis      Redis  `yaml:"redis"`
}

func (conf *Config) Load(file string) (err error) {
	content, err := os.ReadFile(file)
	if err != nil {
		err = fmt.Errorf("read config file error: %v", err)
		return
	}

	return yaml.Unmarshal(content, conf)
}

// PG 配置项
type PG struct {
	Host     string `yaml:"host"`
	Port     int    `yaml:"port"`
	Username string `yaml:"username"`
	Password string `yaml:"password"`
	Database string `yaml:"database"`
	ShowSQL  bool   `yaml:"show_sql"`
}

func (pg *PG) CleanPwd() (pwd string, err error) {
	passwd, err := base64.StdEncoding.DecodeString(pg.Password)
	if err != nil {
		err = fmt.Errorf("pg password clean error:%v", err)
		return
	}
	pwd = string(bytes.TrimSpace(passwd))
	return
}

func (pg *PG) ConnStr() (connStr string, err error) {
	passwd, err := pg.CleanPwd()
	if err != nil {
		return
	}
	connStr = fmt.Sprintf(
		"postgres://%s:%s@%s:%d/%s?sslmode=disable",
		pg.Username, passwd, pg.Host, pg.Port, pg.Database,
	)
	return
}

// Redis 配置项
type Redis struct {
	Host     string `yaml:"host"`
	Port     int    `yaml:"port"`
	Password string `yaml:"password"`
	Database int    `yaml:"database"`
}

func (redis *Redis) CleanPwd() (pwd string, err error) {
	passwd, err := base64.StdEncoding.DecodeString(redis.Password)
	if err != nil {
		err = fmt.Errorf("redis password clean error:%v", err)
		return
	}
	pwd = string(bytes.TrimSpace(passwd))
	return
}

// ------------------------------------------------------------------------------

var cfs = make(map[string]*Config)

// New 解析配置文件(.yaml)。
// 若路径为空，会寻找执行目录下的 config.yaml（windows系统下为 config-win.yaml）
// 该方法会缓存解析过的文件，重复调用依然会返回同样的配置。因此修改配置后，需要重启程序才能生效
func New(file string) (conf *Config, err error) {
	file = strings.TrimSpace(file)
	if file == "" {
		file = "./config.yaml"
		if runtime.GOOS == "windows" {
			var rootPath string
			if _, filename, _, ok := runtime.Caller(0); ok {
				rootPath = path.Dir(path.Dir(filename))
			}

			file = path.Join(rootPath, "config-win.yaml")
		}
	}
	file, err = filepath.Abs(file)
	if err != nil {
		return
	}

	fmt.Println("load config file:", file)

	if _, ok := cfs[file]; ok {
		return cfs[file], nil
	} else {
		conf = new(Config)
		err = conf.Load(file)
		if err != nil {
			return
		}

		cfs[file] = conf
		return
	}
}
